<template>
  <div id="graph"></div>
</template>

<script setup>
import * as d3 from 'd3'
import { onMounted } from 'vue'
import graph from './json'

const { nodes, links } = graph

onMounted(() => {
  initDemo()
})

function initDemo() {
  const w = 600, h = 400
  const color = d3.scaleOrdinal(d3.schemeCategory10)


  // 2、绘制存放节点和关系的svg
  var svg = d3.select('#graph')
    .append('svg')
    .attr('viewBox', [0, 0, w, h])
    .attr('class', 'svg')
    .attr('width', w)
    .attr('height', h)
    .on('dbclick.zoom', () => { return false })  // 禁止双击放大

  svg.call(d3.zoom()
    .scaleExtent([0.2, 5])   // 鼠标缩放的距离, 范围
    .translateExtent([[0, 0], [w, h]])
    .on('start', ()=>{
      svg.style('cursor', 'pointer')
    })
    .on('zoom', zoomed)
    .on('end', ()=>{
      svg.style('cursor', 'default')
    })
  )


  var blues = d3.scaleOrdinal(d3.schemeBlues[3]);


  // 3绘制节点关系
  var lines = svg.append('g')
    .attr('class', 'line')
    .selectAll('line')
    .data(links)
    .enter()
    .append('path')
    .attr('stroke-width', 1)
    .attr('stroke', d => {
      return blues(d.category) || blues(4)
    })


  // 4、绘制节点
  var circles = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('circle')
    .data(nodes)
    .enter()
    .append('circle')
    .attr('r', d => {
      const size = [40,35, 30, 28]
      return size[d.category] || 28
    })
    .attr('fill', (d, i) => {
      return blues(d.category) || blues(4)
    })
    .attr('stroke', 'rgba(120, 60, 60, 0.1)')
    .attr('stroke-width', 5)

  circles.call(d3.drag()
    .on('start', dragStarted)
    .on('drag', draged)
    .on('end', dragEned)
  )



  var texts = svg.append('g')
    .selectAll('text')
    .data(nodes)
    .enter()
    .append('text')
    .text(d => d.name)
    .attr('text-anchor', 'middle')
    .attr('fill', 'rgba(120,33,44,0.8)')
    .attr('font-size', '12')
  texts.call(d3.drag()
    .on('start', dragStarted)
    .on('drag', draged)
    .on('end', dragEned)
  )



  // 1、绘制graph力  
  const simulation = d3.forceSimulation(nodes)
    .alphaDecay(0.01)  // 衰减系数，值越大，图表稳定越快
    .force('collide', d3.forceCollide()      // 碰撞力 防止节点重叠
      .radius(() => 20)
      .iterations(3)
    )
    .force('charge', d3.forceManyBody()  // 引力
      .distanceMax(500)
      .strength(-500)     // 排斥力强度，正值相互吸引，负值相互排斥
    )
    .force('link', d3.forceLink(links)
      .id(d => d.id)
      .distance(160)    // 连接距离
      .strength(1) // 连接力强度 0 ~ 1
      .iterations(2) // 迭代次数
    )
    // .force('x', d3.forceX(w/2))
    // .force('y', d3.forceY(h/2))
    .force('center', d3.forceCenter(w / 2, h / 2))
    .on('tick', () => {
      lines.attr('d', d => {
        if (d.source.x < d.target.x) {
          return `M ${d.source.x} ${d.source.y} L ${d.target.x} ${d.target.y}`
        } else {
          return `M ${d.target.x} ${d.target.y} L ${d.source.x} ${d.source.y}`
        }
      })
      circles.attr('cx', d => d.x).attr('cy', d => d.y)
      texts.attr('x', d => d.x).attr('y', d => d.y)
    })



  function dragStarted(event, d) {
    event.sourceEvent.stopPropagation();
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x
    d.fy = d.y
  }

  function draged(event, d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragEned(event, d) {
    // if (!event.active) simulation.alphaTarget(0);
    d.fx = null
    d.fy = null
  }

  function zoomed(e) {
    svg.selectAll('g').attr('transform',
      'translate(' + e.transform.x + ',' + e.transform.y + ') scale(' + e.transform.k + ')'
    )
  }

  svg.append('text').text('简单可拖拽征未优化版')
    .attr('class', 'title')
    .attr('x', 200)
    .attr('y', 20)

  // end

}



</script>
